{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a374c084-32d0-4677-9d52-99dc0f6b0ab9",
   "metadata": {},
   "source": [
    "### LangChian核心功能：链式调用实现方法\n",
    "#### - 尝试搭建一个简单的链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9e9a8bf9-ac38-4940-8c41-3c38ed5ddc6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv \n",
    "load_dotenv(override=True)\n",
    "\n",
    "DeepSeek_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd276808-820a-42e2-9b8a-269c22dfe639",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "# 使用 DeepSeek 模型\n",
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")  \n",
    "\n",
    "# 直接使用模型 + 输出解析器搭建一个链\n",
    "basic_qa_chain = model | StrOutputParser()\n",
    "# 查看输出结果\n",
    "question = \"你好，请你介绍一下你自己。\"\n",
    "result = basic_qa_chain.invoke(question)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21821fe5-fc97-4e48-baa9-682756131c22",
   "metadata": {},
   "source": [
    "#### - 加入提示词模板创建链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03c48f99",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers.boolean import BooleanOutputParser\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "\n",
    "prompt_template = ChatPromptTemplate([\n",
    "    (\"system\", \"你是一个乐意助人的助手，请根据用户的问题给出回答\"),\n",
    "    (\"user\", \"这是用户的问题： {topic}， 请用 yes 或 no 来回答\")\n",
    "])\n",
    "\n",
    "# 直接使用模型 + 输出解析器\n",
    "bool_qa_chain = prompt_template | model | BooleanOutputParser()\n",
    "# 测试\n",
    "question = \"请问 1 + 1 是否 大于 2？\"\n",
    "result = bool_qa_chain.invoke(question)\n",
    "print(result)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "473b4fe3",
   "metadata": {},
   "source": [
    "#### 借助提示词模板和结果解析器实现功能更加复杂的链\n",
    "至此，我们就搭建了一个非常基础的链。在LangChain中，一个基础的链主要由三部分构成，分别是提示词模板、大模型和结果解析器（结构化解析器）：\n",
    "\n",
    "用户输入\n",
    "\n",
    "↓\n",
    "\n",
    "PromptTemplate → ChatModel → OutputParser\n",
    "（提示词模板） （大模型） （结构化解析）\n",
    "\n",
    "↓\n",
    "\n",
    "构化结果\n",
    "\n",
    "而相比之下，结构化解析器功能最多，一些核心的结构化解析器功能如下：\n",
    "\n",
    "| 解析器名称 | 功能描述 | 类型 |\n",
    "|------------|----------|------|\n",
    "|BooleanOutputParser|将LLM输出解析为布尔值|基础类型解析|\n",
    "|DatetimeOutputParser|将LLM输出解析为日期时间|基础类型解析|\n",
    "|EnumOutputParser|解析输出为预定义枚举值之一|基础类型解析|\n",
    "|RegexParser|使用正则表达式解析LLM输出|模式匹配解析|\n",
    "|RegexDictParser|使用正则表达式将输出解析为字典|模式匹配解析|\n",
    "|StructuredOutputParser|将LLM输出解析为结构化格式|结构化解析|\n",
    "|YamlOutputParser|使用Pydantic模型解析YAML输出|结构化解析|\n",
    "|PandasDataFrameOutputParser|使用Pandas DataFrame格式解析输出|数据处理解析|\n",
    "|CombiningOutputParser|将多个输出解析器组合为一个|组合解析器|\n",
    "|OutputFixingParser|包装解析器并尝试修复解析错误|错误处理解析|\n",
    "|RetryOutputParser|包装解析器并尝试修复解析错误|错误处理解析|\n",
    "|RetryWithErrorOutputParser|包装解析器并尝试修复解析错误|错误处理解析|\n",
    "|ResponseSchema|结构化输出解析器的响应模式|辅助类|"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4242536f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain.output_parsers import ResponseSchema, StructuredOutputParser\n",
    "\n",
    "schemas = [\n",
    "    ResponseSchema(name=\"name\", description=\"用户的姓名\"),\n",
    "    ResponseSchema(name=\"age\", description=\"用户的年龄\")\n",
    "]\n",
    "parser = StructuredOutputParser.from_response_schemas(schemas)\n",
    "\n",
    "prompt = PromptTemplate.from_template(\n",
    "    \"请根据以下内容提取用户信息，并返回 JSON 格式：\\n{input}\\n\\n{format_instructions}\"\n",
    ")\n",
    "\n",
    "chain = (\n",
    "    prompt.partial(format_instructions=parser.get_format_instructions())\n",
    "    | model\n",
    "    | parser\n",
    ")\n",
    "\n",
    "result = chain.invoke({\"input\": \"用户叫李雷，今年25岁，是一名工程师。\"})\n",
    "print(result)  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bacf985b",
   "metadata": {},
   "source": [
    "####  创建复合链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2ef42e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_core.runnables import RunnableSequence\n",
    "from langchain.output_parsers import ResponseSchema, StructuredOutputParser\n",
    "\n",
    "# 第一步：根据标题生成新闻正文\n",
    "news_gen_prompt = PromptTemplate.from_template(\n",
    "    \"请根据以下新闻标题撰写一段简短的新闻内容（100字以内）：\\n\\n标题：{title}\"\n",
    ")\n",
    "\n",
    "# 第一个子链：生成新闻内容\n",
    "news_chain = news_gen_prompt | model\n",
    "\n",
    "# 第二步：从正文中提取结构化字段\n",
    "schemas = [\n",
    "    ResponseSchema(name=\"time\", description=\"事件发生的时间\"),\n",
    "    ResponseSchema(name=\"location\", description=\"事件发生的地点\"),\n",
    "    ResponseSchema(name=\"event\", description=\"发生的具体事件\"),\n",
    "]\n",
    "parser = StructuredOutputParser.from_response_schemas(schemas)\n",
    "\n",
    "summary_prompt = PromptTemplate.from_template(\n",
    "    \"请从下面这段新闻内容中提取关键信息，并返回结构化JSON格式：\\n\\n{news}\\n\\n{format_instructions}\"\n",
    ")\n",
    "\n",
    "# 第二个子链：生成新闻摘要\n",
    "summary_chain = (\n",
    "    summary_prompt.partial(format_instructions=parser.get_format_instructions())\n",
    "    | model\n",
    "    | parser\n",
    ")\n",
    "\n",
    "# 组合成一个复合 Chain\n",
    "full_chain = news_chain | summary_chain\n",
    "\n",
    "# 调用复合链\n",
    "result = full_chain.invoke({\"title\": \"苹果公司在加州发布新款AI芯片\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5413f2b8",
   "metadata": {},
   "source": [
    "#### 借助LangChain适配器设置自定义可运行的节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86bb5aa1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableLambda\n",
    "\n",
    "# 一个简单的打印函数，调试用\n",
    "def debug_print(x):\n",
    "    print(\"中间结果（新闻正文）:\", x)\n",
    "    return x\n",
    "\n",
    "debug_node = RunnableLambda(debug_print)\n",
    "\n",
    "# 插入 debug 节点\n",
    "full_chain = news_chain | debug_node | summary_chain\n",
    "# 调用复合链\n",
    "result = full_chain.invoke({\"title\": \"苹果公司在加州发布新款AI芯片\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d0f6737",
   "metadata": {},
   "source": [
    "#### 构建流式智能问答系统"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42befa80",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "\n",
    "chatbot_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你叫小智，是一名乐于助人的助手。\"),\n",
    "    (\"user\", \"{input}\")\n",
    "])\n",
    "\n",
    "# 使用 DeepSeek 模型\n",
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")  \n",
    "\n",
    "# 直接使用提示模版 +模型 + 输出解析器\n",
    "qa_chain_with_system = chatbot_prompt | model | StrOutputParser()\n",
    "\n",
    "# 异步实现流式输出\n",
    "async for chunk in qa_chain_with_system.astream({\"input\": \"你好，请你介绍一下你自己\"}):\n",
    "    print(chunk, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae222d81",
   "metadata": {},
   "source": [
    "#### 构建多轮对话的流式智能问答系统"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a7a7ba43",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "\n",
    "chatbot_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你叫小智，是一名乐于助人的助手。\"),\n",
    "    (\"user\", \"{input}\")\n",
    "])\n",
    "\n",
    "# 使用 DeepSeek 模型\n",
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")  \n",
    "\n",
    "# 直接使用模型 + 输出解析器\n",
    "basic_qa_chain = chatbot_prompt | model | StrOutputParser()\n",
    "\n",
    "# 测试\n",
    "question = \"你好，请你介绍一下你自己。\"\n",
    "result = basic_qa_chain.invoke(question)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5db813a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 记忆对话\n",
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "chatbot_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        SystemMessage(\n",
    "            content=\"你叫小智，是一名乐于助人的助手。\"\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "basic_qa_chain = chatbot_prompt | model | StrOutputParser()\n",
    "# 增加对话\n",
    "messages_list = [\n",
    "    HumanMessage(content=\"你好，我叫陈明，好久不见。\"),\n",
    "    AIMessage(content=\"你好呀！我是小智，一名乐于助人的AI助手。很高兴认识你！\"),\n",
    "]\n",
    "\n",
    "question = \"你好，请问我叫什么名字。\"\n",
    "# 组合\n",
    "messages_list.append(HumanMessage(content=question))\n",
    "# 调用链\n",
    "result = basic_qa_chain.invoke({\"messages\": messages_list})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1220abc9",
   "metadata": {},
   "source": [
    "#### 完整的多轮对话函"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc450241",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "model  = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")\n",
    "parser = StrOutputParser()\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    SystemMessage(content=\"你叫小智，是一名乐于助人的助手。\"),\n",
    "    MessagesPlaceholder(variable_name=\"messages\"),\n",
    "])\n",
    "\n",
    "chain = prompt | model | parser\n",
    "\n",
    "messages_list = []  # 初始化历史\n",
    "print(\"🔹 输入 exit 结束对话\")\n",
    "while True:\n",
    "    user_query = input(\"👤 你：\")\n",
    "    if user_query.lower() in {\"exit\", \"quit\"}:\n",
    "        break\n",
    "\n",
    "    # 1) 追加用户消息\n",
    "    messages_list.append(HumanMessage(content=user_query))\n",
    "\n",
    "    # 2) 调用模型 等待结果\n",
    "    assistant_reply = chain.invoke({\"messages\": messages_list})\n",
    "    print(\"🤖 小智：\", assistant_reply)\n",
    "    # 2) 调用模型 流式回复\n",
    "    async for chunk in chain.astream({\"messages\": messages_list}):\n",
    "        print(chunk, end=\"\", flush=True)\n",
    "    # 3) 追加 AI 回复\n",
    "    messages_list.append(AIMessage(content=assistant_reply))\n",
    "\n",
    "    # 4) 仅保留最近 50 条\n",
    "    messages_list = messages_list[-50:]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5ac9e35",
   "metadata": {},
   "source": [
    "#### 用gradio来开发一个支持在网页上进行交互的问答机器人"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b49161b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 安装 Gradio\n",
    "! uv add gradio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b150b8fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n",
      "C:\\Users\\李林\\AppData\\Local\\Temp\\ipykernel_17452\\795933737.py:53: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style dictionaries with 'role' and 'content' keys.\n",
      "  chatbot = gr.Chatbot(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* Running on local URL:  http://0.0.0.0:7860\n",
      "* To create a public link, set `share=True` in `launch()`.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div><iframe src=\"http://localhost:7860/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Keyboard interruption in main thread... closing server.\n"
     ]
    },
    {
     "data": {
      "text/plain": []
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import gradio as gr\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "# 初始化模型\n",
    "model = init_chat_model(\"deepseek-chat\", model_provider=\"deepseek\")\n",
    "\n",
    "# 创建问答链\n",
    "system_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你叫小智，是一名乐于助人的助手。\"),\n",
    "    (\"human\", \"{input}\")\n",
    "])\n",
    "\n",
    "qa_chain = system_prompt | model | StrOutputParser()\n",
    "\n",
    "# 流式回应函数\n",
    "async def chat_response(message, history):\n",
    "    \"\"\"流式生成AI回应\"\"\"\n",
    "    partial_message = \"\"\n",
    "    \n",
    "    async for chunk in qa_chain.astream({\"input\": message}):\n",
    "        partial_message += chunk\n",
    "        yield partial_message\n",
    "\n",
    "# 创建 Gradio 界面\n",
    "def create_chatbot():\n",
    "    # 自定义CSS样式 - 居中显示\n",
    "    css = \"\"\"\n",
    "    .main-container {\n",
    "        max-width: 1200px;\n",
    "        margin: 0 auto;\n",
    "        padding: 20px;\n",
    "    }\n",
    "    .header-text {\n",
    "        text-align: center;\n",
    "        margin-bottom: 20px;\n",
    "    }\n",
    "    \"\"\"\n",
    "    \n",
    "    with gr.Blocks(title=\"DeepSeek Chat\", css=css) as demo:\n",
    "        with gr.Column(elem_classes=[\"main-container\"]):\n",
    "            # 居中显示标题\n",
    "            gr.Markdown(\n",
    "                \"# 🤖 LangChain B站公开课 By九天Hector \", \n",
    "                elem_classes=[\"header-text\"]\n",
    "            )\n",
    "            gr.Markdown(\n",
    "                \"基于 LangChain LCEL 构建的流式对话机器人\", \n",
    "                elem_classes=[\"header-text\"]\n",
    "            )\n",
    "            \n",
    "            chatbot = gr.Chatbot(\n",
    "                height=500,\n",
    "                show_copy_button=True,\n",
    "                avatar_images=(\n",
    "                    \"https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f464.png\",\n",
    "                    \"https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f916.png\"\n",
    "                ),\n",
    "                \n",
    "            )\n",
    "            \n",
    "            with gr.Row():\n",
    "                msg = gr.Textbox(\n",
    "                    placeholder=\"请输入您的问题...\",\n",
    "                    container=False,\n",
    "                    scale=7\n",
    "                )\n",
    "                submit = gr.Button(\"发送\", scale=1, variant=\"primary\")\n",
    "                clear = gr.Button(\"清空\", scale=1)\n",
    "        \n",
    "        # 处理消息发送\n",
    "        async def respond(message, chat_history):\n",
    "            if not message.strip():\n",
    "                yield \"\", chat_history\n",
    "                return\n",
    "            \n",
    "            # 1. 添加用户消息到历史并立即显示\n",
    "            chat_history = chat_history + [(message, None)]\n",
    "            yield \"\", chat_history  # 立即显示用户消息\n",
    "            \n",
    "            # 2. 流式生成AI回应\n",
    "            async for response in chat_response(message, chat_history):\n",
    "                # 更新最后一条消息的AI回应\n",
    "                chat_history[-1] = (message, response)\n",
    "                yield \"\", chat_history\n",
    "        \n",
    "        # 清空对话历史的函数\n",
    "        def clear_history():\n",
    "            return [], \"\"\n",
    "        \n",
    "        # 绑定事件\n",
    "        msg.submit(respond, [msg, chatbot], [msg, chatbot])\n",
    "        submit.click(respond, [msg, chatbot], [msg, chatbot])\n",
    "        clear.click(clear_history, outputs=[chatbot, msg])\n",
    "    \n",
    "    return demo\n",
    "\n",
    "# 启动界面\n",
    "demo = create_chatbot()\n",
    "demo.launch(\n",
    "    server_name=\"0.0.0.0\",\n",
    "    server_port=7860,\n",
    "    share=False,\n",
    "    debug=True\n",
    ")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08dc90c6",
   "metadata": {},
   "source": [
    "## gradio + 记忆 +  流式输出 完整实现的代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2457426f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gradio as gr\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_core.messages import SystemMessage, HumanMessage, AIMessage\n",
    "\n",
    "# ──────────────────────────────────────────────\n",
    "# 1. 模型、Prompt、Chain\n",
    "# ──────────────────────────────────────────────\n",
    "model = init_chat_model(\"deepseek-chat\", model_provider=\"deepseek\")\n",
    "parser = StrOutputParser()\n",
    "\n",
    "chatbot_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        SystemMessage(content=\"你叫小智，是一名乐于助人的助手。\"),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),  # 手动传入历史\n",
    "    ]\n",
    ")\n",
    "\n",
    "qa_chain = chatbot_prompt | model | parser   # LCEL 组合\n",
    "\n",
    "# ──────────────────────────────────────────────\n",
    "# 2. Gradio 组件\n",
    "# ──────────────────────────────────────────────\n",
    "CSS = \"\"\"\n",
    ".main-container {max-width: 1200px; margin: 0 auto; padding: 20px;}\n",
    ".header-text {text-align: center; margin-bottom: 20px;}\n",
    "\"\"\"\n",
    "\n",
    "def create_chatbot() -> gr.Blocks:\n",
    "    with gr.Blocks(title=\"DeepSeek Chat\", css=CSS) as demo:\n",
    "        with gr.Column(elem_classes=[\"main-container\"]):\n",
    "            gr.Markdown(\"# 🤖 LangChain B站公开课 By九天Hector\", elem_classes=[\"header-text\"])\n",
    "            gr.Markdown(\"基于 LangChain LCEL 构建的流式对话机器人\", elem_classes=[\"header-text\"])\n",
    "\n",
    "            chatbot = gr.Chatbot(\n",
    "                height=500,\n",
    "                show_copy_button=True,\n",
    "                avatar_images=(\n",
    "                    \"https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f464.png\",\n",
    "                    \"https://cdn.jsdelivr.net/gh/twitter/twemoji@v14.0.2/assets/72x72/1f916.png\",\n",
    "                ),\n",
    "            )\n",
    "            msg = gr.Textbox(placeholder=\"请输入您的问题...\", container=False, scale=7)\n",
    "            submit = gr.Button(\"发送\", scale=1, variant=\"primary\")\n",
    "            clear = gr.Button(\"清空\", scale=1)\n",
    "\n",
    "        # ---------------  状态：保存 messages_list  ---------------\n",
    "        state = gr.State([])          # 这里存放真正的 Message 对象列表\n",
    "\n",
    "        # ---------------  主响应函数（流式） ----------------------\n",
    "        async def respond(user_msg: str, chat_hist: list, messages_list: list):\n",
    "            # 1) 输入为空直接返回\n",
    "            if not user_msg.strip():\n",
    "                yield \"\", chat_hist, messages_list\n",
    "                return\n",
    "\n",
    "            # 2) 追加用户消息\n",
    "            messages_list.append(HumanMessage(content=user_msg))\n",
    "            chat_hist = chat_hist + [(user_msg, None)]\n",
    "            yield \"\", chat_hist, messages_list      # 先显示用户消息\n",
    "\n",
    "            # 3) 流式调用模型\n",
    "            partial = \"\"\n",
    "            async for chunk in qa_chain.astream({\"messages\": messages_list}):\n",
    "                partial += chunk\n",
    "                # 更新最后一条 AI 回复\n",
    "                chat_hist[-1] = (user_msg, partial)\n",
    "                yield \"\", chat_hist, messages_list\n",
    "\n",
    "            # 4) 完整回复加入历史，裁剪到最近 50 条\n",
    "            messages_list.append(AIMessage(content=partial))\n",
    "            messages_list = messages_list[-50:]\n",
    "\n",
    "            # 5) 最终返回（Gradio 需要把新的 state 传回）\n",
    "            yield \"\", chat_hist, messages_list\n",
    "\n",
    "        # ---------------  清空函数 -------------------------------\n",
    "        def clear_history():\n",
    "            return [], \"\", []          # 清空 Chatbot、输入框、messages_list\n",
    "\n",
    "        # ---------------  事件绑定 ------------------------------\n",
    "        msg.submit(respond, [msg, chatbot, state], [msg, chatbot, state])\n",
    "        submit.click(respond, [msg, chatbot, state], [msg, chatbot, state])\n",
    "        clear.click(clear_history, outputs=[chatbot, msg, state])\n",
    "\n",
    "    return demo\n",
    "\n",
    "\n",
    "# ──────────────────────────────────────────────\n",
    "# 3. 启动应用\n",
    "# ──────────────────────────────────────────────\n",
    "demo = create_chatbot()\n",
    "demo.launch(server_name=\"0.0.0.0\", server_port=7860, share=False, debug=True)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
